﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PlaceHR.Processor.Processor.Salary.Steps;

namespace PlaceHR.Processor.Processor
{
    class SalaryProcessor : ProcessorBase<SalaryProcessorContext, EmployeeSalary>
    {
        #region Private Methods

        protected override SalaryProcessorContext LoadCurrentContext(Employee employee, IEngine currentEngine)
        {
            if (employee == null || currentEngine == null)
            {
                throw new Exception("employee or currentEngine is null!");
            }

            SalaryProcessorContext context = CurrentContext;

            if (context == null)
            {
                context = new SalaryProcessorContext
                {
                    CurrentEmployee = employee,
                    CurrentEngine = currentEngine,
                    HRDBContext = currentEngine.HRDBContext
                };
            }

            this.LoadGlobalSettings(employee, currentEngine, context);
            this.LoadSalarySettings(employee, currentEngine, context);
            this.LoadPersonalIncomeTaxSettings(employee, currentEngine, context);
            this.LoadContractInfo(employee, currentEngine, context);

            return context;
        }

        protected override EmployeeSalary LoadCurrentEntity(Employee employee, IEngine currentEngine)
        {
            EmployeeSalary employeeSalary = CurrentContext.CurrentEngine.HRDBContext.EmployeeSalary.FirstOrDefault(
                s => s.EmployeeID == employee.EmployeeID 
                    && s.Year.Value == CurrentContext.CurrentEngine.Year 
                    && s.Month.Value == CurrentContext.CurrentEngine.Month 
                    && !s.IsDeleted);
            if (employeeSalary != null && employeeSalary.EmployeeSalaryID > 0)
            {
                if (CurrentContext.CurrentEngine.ForceRecalculate)
                {
                    Console.WriteLine(string.Format("删除{0}员工的旧记录...", employeeSalary.EmployeeID));
                    //重新计算，这时候要先删除已有记录
                    foreach (var child in employeeSalary.EmployeeSalaryDetail.ToList())
                    {
                        //employeeSalary.EmployeeSalaryDetail.Remove(child);
                        CurrentContext.CurrentEngine.HRDBContext.DeleteObject(child);
                    }

                    CurrentContext.CurrentEngine.HRDBContext.EmployeeSalary.DeleteObject(employeeSalary);
                    CurrentContext.CurrentEngine.HRDBContext.SaveChanges();
                }
                else
                { 
                    //不需要重新计算，processor终止！
                    return null;
                }
            }

            employeeSalary = new EmployeeSalary
            {
                EmployeeID = CurrentContext.CurrentEmployee.EmployeeID,
                Year = (short?)CurrentContext.CurrentEngine.Year,
                Month = (short?)CurrentContext.CurrentEngine.Month,
                UpdateBy = 0,
                IsDeleted = false,
                UpdateTime = DateTime.Now,
                //TDB: which day?
                SalaryPaidDate = DateTime.Now
            };

            employeeSalary.EmployeeSalaryDetail.Add(new EmployeeSalaryDetail());
            this.InitializeSalaryValues(employeeSalary);
            currentEngine.HRDBContext.EmployeeSalary.AddObject(employeeSalary);

            return employeeSalary;
        }

        protected override void RegisterStepCalculators()
        {
            this.Register(new BasicSalaryStepCalculator());
            this.Register(new BonusSalaryStepCalculator());
            this.Register(new OtherSalaryStepCalculator());
            this.Register(new UnpaidLeaveSalaryStepCalculator());
            this.Register(new SickLeaveSalaryStepCalculator());
            this.Register(new WelfareSalaryStepCalculator());
            this.Register(new TaxSalaryStepCalculator());
            this.Register(new FinalizeSalaryStepCalculator());
        }

        public override bool IsValidForProcess
        {
            get {
                return this.CurrentContext != null 
                    && this.CurrentEntity != null 
                    && this.CurrentContext.CurrentSalarySettings != null
                    && this.CurrentContext.CurrentContractInfo != null;
            }
        }

        private void LoadGlobalSettings(Employee employee, IEngine currentEngine, SalaryProcessorContext context)
        {
            GlobalSettings defaultGlobalSetting = currentEngine.HRDBContext.GlobalSettings.FirstOrDefault(p => p.OfficeCode == null);

            string officeCode = employee.OfficeDepartment_lnk.FirstOrDefault().OfficeCode;
            GlobalSettings currentGlobalSetting = currentEngine.HRDBContext.GlobalSettings.FirstOrDefault(p => p.OfficeCode != null && p.OfficeCode.Equals(officeCode, StringComparison.OrdinalIgnoreCase));

            if (defaultGlobalSetting == null || currentGlobalSetting == null)
            {
                throw new Exception("Global setting is invalid");
            }

            context.WorkingDaysPerMonth = currentGlobalSetting.WorkingDaysPerMonth.HasValue ? currentGlobalSetting.WorkingDaysPerMonth.Value : defaultGlobalSetting.WorkingDaysPerMonth.Value;
            context.WorkingHoursPerDay = currentGlobalSetting.WorkingHoursPerDay.HasValue ? currentGlobalSetting.WorkingHoursPerDay.Value : defaultGlobalSetting.WorkingHoursPerDay.Value;

            if (employee.EmployeeTypeCode != null && !employee.EmployeeTypeCode.Equals("Local", StringComparison.OrdinalIgnoreCase))
            {
                context.PersonalIncomeTaxbase = currentGlobalSetting.ForeignerIncomeTaxBase.HasValue ? currentGlobalSetting.ForeignerIncomeTaxBase.Value : defaultGlobalSetting.ForeignerIncomeTaxBase.Value;
            }
            else
            {
                context.PersonalIncomeTaxbase = currentGlobalSetting.PersonalIncomeTaxBase.HasValue ? currentGlobalSetting.PersonalIncomeTaxBase.Value : defaultGlobalSetting.PersonalIncomeTaxBase.Value;
            }

            context.AverageSalary = currentGlobalSetting.AverageSalary.HasValue ? currentGlobalSetting.AverageSalary.Value : defaultGlobalSetting.AverageSalary.Value;
            context.MinimumWage = currentGlobalSetting.MinimumWage.HasValue ? currentGlobalSetting.MinimumWage.Value : defaultGlobalSetting.MinimumWage.Value;
        }

        private void LoadSalarySettings(Employee employee, IEngine currentEngine, SalaryProcessorContext context)
        {
            string officeCode = employee.OfficeDepartment_lnk.FirstOrDefault().OfficeCode;
            context.CurrentSalarySettings = currentEngine.HRDBContext.SalarySettings.FirstOrDefault(
                p => p.Office != null && p.Office.Equals(officeCode, StringComparison.OrdinalIgnoreCase) && p.HuKou.Equals(employee.HuKou, StringComparison.OrdinalIgnoreCase));

            context.CurrentSickSalarySettingList = currentEngine.HRDBContext.SickSalarySettings.Where(
                p => p.OfficeCode == null || p.OfficeCode.Equals(officeCode, StringComparison.OrdinalIgnoreCase)).ToList();

            context.CurrentSickSalaryRestriction = currentEngine.HRDBContext.SickSalaryRestriction.FirstOrDefault(
                p => p.OfficeCode == null || p.OfficeCode.Equals(officeCode, StringComparison.OrdinalIgnoreCase));
        }

        private void LoadPersonalIncomeTaxSettings(Employee employee, IEngine currentEngine, SalaryProcessorContext context)
        {
            string officeCode = employee.OfficeDepartment_lnk.FirstOrDefault().OfficeCode;
            context.IncomeTaxSettings = currentEngine.HRDBContext.PersonalIncomeTaxSettings.ToList();
        }

        private void LoadContractInfo(Employee employee, IEngine currentEngine, SalaryProcessorContext context)
        {
            context.CurrentContractInfo = employee.ContractInfo.FirstOrDefault();
        }

        private void InitializeSalaryValues(EmployeeSalary employeeSalary)
        {
            if (employeeSalary == null)
            {
                return;
            }

            employeeSalary.BasicSalary = 0;
            employeeSalary.Bonus = 0;
            employeeSalary.ExpatAllowance = 0;
            employeeSalary.HousingFundCompany = 0;
            employeeSalary.HousingFundIndividual = 0;
            employeeSalary.IndividualIncomeTax = 0;
            employeeSalary.NetSalary = 0;
            //employeeSalary.Others = 0;
            employeeSalary.PositionSalary = 0;
            employeeSalary.SickLeave = 0;
            employeeSalary.SocialInsuranceCompany = 0;
            employeeSalary.SocialInsuranceIndividual = 0;
            employeeSalary.TaxableSalary = 0;
            employeeSalary.UnpaidLeave = 0;

            var detail = employeeSalary.EmployeeSalaryDetail.FirstOrDefault();
            if (detail != null)
            {
                detail.Injury_Company_Money = 0;
                detail.Injury_Company_Percent = 0;
                detail.Injury_Personal_Money = 0;
                detail.Injury_Personal_Percent = 0;

                detail.Maternity_Company_Money = 0;
                detail.Maternity_Company_Percent = 0;
                detail.Maternity_Personal_Money = 0;
                detail.Maternity_Personal_Percent = 0;

                detail.Medical_Company_Money = 0;
                detail.Medical_Company_Percent = 0;
                detail.Medical_Personal_Money = 0;
                detail.Medical_Personal_Percent = 0;

                detail.Pension_Company_Money = 0;
                detail.Pension_Company_Percent = 0;
                detail.Pension_Personal_Money = 0;
                detail.Pension_Personal_Percent = 0;

                detail.SeriousIllness_Company_Money = 0;
                detail.SeriousIllness_Company_Percent = 0;
                detail.SeriousIllness_Personal_Money = 0;
                detail.SeriousIllness_Personal_Percent = 0;

                detail.Social_Company_Money = 0;
                detail.Social_Company_Percent = 0;
                detail.Social_Personal_Money = 0;
                detail.Social_Personal_Percent = 0;

                detail.Fund_Company_Money = 0;
                detail.Fund_Company_Percent = 0;
                detail.Fund_Personal_Money = 0;
                detail.Fund_Personal_Percent = 0;

                detail.Unemployment_Company_Money = 0;
                detail.Unemployment_Company_Percent = 0;
                detail.Unemployment_Personal_Money = 0;
                detail.Unemployment_Personal_Percent = 0;

                detail.UpdateBy = 0;
                detail.UpdateDate = DateTime.Now;
            }
        }

        #endregion
    }
}
